package com.hd123.rumba.gwt.widget.client;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ChangeListenerCollection;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.PopupListener;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.hd123.rumba.gwt.widget.client.grid.DxGrid;
import com.hd123.rumba.gwt.widget.client.grid.DxGridColumnDef;
import com.hd123.rumba.gwt.widget.client.grid.DxGridListener;
import com.hd123.rumba.gwt.widget.client.grid.SinglePageDataProvider;
import com.hd123.rumba.gwt.widget.client.grid.StringRendererFactory;

/**
 * F3或ALT+DOWN下拉.
 * 
 * @author caili
 * 
 */
public class ComboBox extends Composite implements SourcesChangeEvents, KeyboardListener,
    ClickListener, PopupListener, ChangeListener {
  private class NameValue {
    String name;
    Object value;

    NameValue(String name, Object value) {
      this.name = name;
      this.value = value;
    }
  }

  ArrayList/* <NameValue> */items = new ArrayList/* <NameValue> */();
  TextBox codeBox;
  Image dropDownButton;
  Popup popup;
  boolean readOnly = false;
  /**
   * 输入框中的内容可修改, 或不可修改(只能选择列表)
   */
  boolean editable = false;

  public class Provider extends SinglePageDataProvider {

    ArrayList/* <String> */values;

    public Provider() {
      super();
    }

    public Provider(ArrayList/* <String> */values) {
      this();
      this.values = values;
    }

    public void setValues(ArrayList/* <String> */values) {
      this.values = values;
    }

    public int getColumnCount() {
      return 1;
    }

    public String getData(int row, int col) {
      if (values.size() > 0 && row < values.size())
        return (String) values.get(row);
      return "";
    }

    public int getRowCount() {
      return values.size();
    }
  }

  public class Popup extends PopupPanel implements KeyboardListener, DxGridListener {
    Provider provider;
    DxGrid grid;
    Object selectedObject;

    public Popup() {
      super(true, true);
      setStyleName("jdgwt-CNBBrowser");

      provider = new Provider();
      grid = new DxGrid(1, 1);
      grid.setProvider(provider);
      grid.setCompact(true);
      DxGridColumnDef cd;
      grid.addColumnDef(cd = new DxGridColumnDef(""));
      cd.setRendererFactory(new StringRendererFactory());

      super.setWidget(grid);

      grid.addKeyboardListener(this);
      grid.addDxGridListener(this);
    }

    public Popup(ArrayList/* <String> */items) {
      this();
      setItems(items);
    }

    public void setItems(ArrayList/* <String> */items) {
      provider.values = items;
    }

    public Object getSelectedObject() {
      return selectedObject;
    }

    public void onLoad() {
      selectedObject = null;
      grid.refresh();
      grid.setFocus(true);
    }

    public void onKeyDown(Widget sender, char keyCode, int modifiers) {
    }

    public void onKeyPress(Widget sender, char keyCode, int modifiers) {
      if (sender == grid) {
        if (keyCode == KEY_ENTER) {
          if (grid.getCurrentRow() < provider.getRowCount()) {
            selectedObject = provider.getData(grid.getCurrentRow(), 0);
          }
          popup.hide();
        } else if (keyCode == KEY_ESCAPE) {
          popup.hide();
        }
      }
    }

    public void onKeyUp(Widget sender, char keyCode, int modifiers) {
    }

    public void afterRefresh(DxGrid theGrid) {
      theGrid.setFocus(true);
    }

    public void onCellDblClick(DxGrid theGrid, int row, int col) {
      if (theGrid == grid) {
        if (row < provider.getRowCount()) {
          selectedObject = provider.getData(row, 0);
          popup.hide();
        }
      }
    }
  }

  public ComboBox() {
    super();
    HorizontalPanel hp = new HorizontalPanel();
    hp.setSpacing(0);
    hp.add(codeBox = createCodeBox());
    hp.setCellWidth(codeBox, "100%");// //////
    hp.add(dropDownButton = new Image("images/dropdown.gif"));
    initWidget(hp);
    codeBox.addKeyboardListener(this);
    codeBox.addChangeListener(this);
    dropDownButton.addClickListener(this);
  }

  protected TextBox createCodeBox() {
    return new TextBox();
  }

  public String getText() {
    return codeBox.getText();
  }

  public void setText(String text) {
    codeBox.setText(text);
  }

  public void addItem(String text) {
    items.add(new NameValue(text, null));
  }

  public void clearItems() {
    items.clear();
  }

  public ArrayList/* <String> */getItems() {
    ArrayList/* <String> */result = new ArrayList/* <String> */();
    for (int i = 0; i < items.size(); ++i)
      result.add(((NameValue) items.get(i)).name);
    return result;
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public void setReadOnly(boolean readOnly) {
    this.readOnly = readOnly;
    codeBox.setStyleName(readOnly ? "gwt-TextBox-Disabled" : "gwt-TextBox");
    dropDownButton.setVisible(!readOnly);
  }

  public boolean isEditable() {
    return editable;
  }

  public void setEditable(boolean editable) {
    this.editable = editable;
  }

  public TextBox getCodeBox() {
    return codeBox;
  }

  public Image getDropDownButton() {
    return dropDownButton;
  }

  public void setFocus(boolean focused) {
    Util.focusTextBox(codeBox);
  }

  public void setWidth(String width) {
    super.setWidth(width);
    codeBox.setWidth("100%");
  }

  public void setMaxLength(int length) {
    codeBox.setMaxLength(length);
  }

  private boolean isDropping = false;

  public void dropDown() {
    isDropping = true;
    if (popup == null) {
      popup = new Popup();
      popup.setWidth(codeBox.getOffsetWidth() + "px");
      popup.addPopupListener(this);
    }
    popup.setItems(getItems());
    codeBox.setFocus(false);
    popup.setPopupPosition(codeBox.getAbsoluteLeft(), codeBox.getAbsoluteTop()
        + codeBox.getOffsetHeight());
    popup.show();
  }

  public void onPopupClosed(PopupPanel sender, boolean autoClosed) {
    if (sender == popup) {
      String s = (String) popup.getSelectedObject();
      if (s != null) {
        String oldValue = codeBox.getText();
        codeBox.setText(s);
        if (!s.equals(oldValue))
          fireChange();
      }
      Util.focusTextBox(codeBox);
      isDropping = false;
    }
  }

  public void onKeyDown(Widget sender, char keyCode, int modifiers) {
    if (sender == codeBox) {
      if ((isReadOnly() || !isEditable()) && !Util.isNavKey(keyCode) && modifiers == 0) {
        codeBox.cancelKey();
        return;
      } else if (keyCode == Util.KEY_DROPDOWN || keyCode == KEY_DOWN
          && (modifiers & MODIFIER_ALT) != 0) {
        dropDown();
      }
    }
  }

  public void onKeyPress(Widget sender, char keyCode, int modifiers) {
    if (sender == codeBox && (isReadOnly() || !isEditable()) && !Util.isNavKey(keyCode)) {
      codeBox.cancelKey();
    }
  }

  public void onKeyUp(Widget sender, char keyCode, int modifiers) {
    if (sender == codeBox && (isReadOnly() || !isEditable()) && !Util.isNavKey(keyCode))
      codeBox.cancelKey();
  }

  public void onClick(Widget sender) {
    if (sender == dropDownButton) {
      dropDown();
    }
  }

  ChangeListenerCollection changeListeners;

  public void addChangeListener(ChangeListener listener) {
    if (changeListeners == null) {
      changeListeners = new ChangeListenerCollection();
    }
    changeListeners.add(listener);
  }

  public void removeChangeListener(ChangeListener listener) {
    if (changeListeners != null)
      changeListeners.remove(listener);
  }

  public void fireChange() {
    if (changeListeners != null)
      for (Iterator it = changeListeners.iterator(); it.hasNext();) {
        ChangeListener listener = (ChangeListener) it.next();
        listener.onChange(this);
      }
  }

  public void onChange(Widget sender) {
    if (sender == codeBox && !isDropping)
      fireChange();
  }

  /**
   * @return 选中的或输入的项的name
   */
  public String getName() {
    return getText();
  }

  /**
   * @return 和输入或选中的item匹配的value. 如果没有找到和输入的匹配的name, 则返回null
   */
  public Object getItemValue() {
    return getItemValue(getSelectedIndex());
  }

  /**
   * @param index
   *            0-based
   * @return index指定的name. index如果超出范围, 返回null
   */
  public String getName(int index) {
    if (index >= 0 && index < items.size())
      return ((NameValue) items.get(index)).name;
    return null;
  }

  /**
   * @param index
   *            0-based
   * @return index指定的value. index如果超出范围, 返回null
   */
  public Object getItemValue(int index) {
    if (index >= 0 && index < items.size())
      return ((NameValue) items.get(index)).value;
    return null;
  }

  public void addItem(String name, Object value) {
    items.add(new NameValue(name, value));
  }

  /**
   * @return 如果没有选中的, 或输入的值找不到和列表中的匹配的, 返回-1. index is 0-based
   */
  public int getSelectedIndex() {
    String name = getName();
    if (name != null) {
      for (int i = 0; i < items.size(); ++i) {
        if (name.equals(((NameValue) items.get(i)).name))
          return i;
      }
    }
    return -1;
  }
}
